home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / littlest / littl-st.lha / interp.c < prev    next >
C/C++ Source or Header  |  1993-08-10  |  18KB  |  580 lines

  1. /*
  2.     Little Smalltalk version 3
  3.     Written by Tim Budd, Oregon State University, July 1988
  4.  
  5.     bytecode interpreter module
  6.  
  7.     given a process object, execute bytecodes in a tight loop.
  8.  
  9.     performs subroutine calls for
  10.         a) finding a non-cached method
  11.         b) executing a primitive
  12.  
  13.     otherwise simply loops until time slice has ended
  14. */
  15.  
  16. # include <stdio.h>
  17. # include "env.h"
  18. # include "memory.h"
  19. # include "names.h"
  20. # include "interp.h"
  21.  
  22. object trueobj, falseobj;
  23. boolean watching = 0;
  24. extern object primitive( INT X OBJP );
  25.  
  26. /*
  27.     the following variables are local to this module
  28. */
  29.  
  30. static object method, messageToSend;
  31.  
  32. static int messTest(obj)
  33. object obj;
  34. {
  35.     return obj == messageToSend;
  36. }
  37.  
  38. /* a cache of recently executed methods is used for fast lookup */
  39. # define cacheSize 211
  40. static struct {
  41.     object cacheMessage;    /* the message being requested */
  42.     object lookupClass;    /* the class of the receiver */
  43.     object cacheClass;    /* the class of the method */
  44.     object cacheMethod;    /* the method itself */
  45.     } methodCache[cacheSize];
  46.  
  47. /* flush an entry from the cache (usually when its been recompiled) */
  48. flushCache(messageToSend, class)
  49. object messageToSend, class;
  50. {    int hash;
  51.  
  52.     hash = (((int) messageToSend) + ((int) class)) / cacheSize;
  53.     methodCache[hash].cacheMessage = nilobj;
  54. }
  55.     
  56. /*
  57.     findMethod
  58.         given a message and a class to start looking in,
  59.         find the method associated with the message
  60. */
  61. static boolean findMethod(methodClassLocation)
  62. object *methodClassLocation;
  63. {    object methodTable, methodClass;
  64.  
  65.     method = nilobj;
  66.     methodClass = *methodClassLocation;
  67.  
  68.     for (; methodClass != nilobj; methodClass = 
  69.             basicAt(methodClass, superClassInClass)) {
  70.         methodTable = basicAt(methodClass, methodsInClass);
  71.         method = hashEachElement(methodTable, messageToSend, messTest);
  72.         if (method != nilobj)
  73.             break;
  74.         }
  75.  
  76.     if (method == nilobj) {        /* it wasn't found */
  77.         methodClass = *methodClassLocation;
  78.         return false;
  79.         }
  80.  
  81.     *methodClassLocation = methodClass;
  82.     return true;
  83. }
  84.  
  85. # define nextByte() *(bp + byteOffset++)
  86. # define ipush(x) incr(*++pst=(x))
  87. # define stackTop() *pst
  88. # define stackTopPut(x) decr((*pst)); incr((*pst = x))
  89. # define stackTopFree() decr((*pst)); *pst-- = nilobj
  90. /* note that ipop leaves x with excess reference count */
  91. # define ipop(x) x = stackTop(); *pst-- = nilobj
  92. # define processStackTop() ((pst-psb)+1)
  93. # define receiverAt(n) *(rcv+n)
  94. # define receiverAtPut(n,x) decr(receiverAt(n)); incr(receiverAt(n)=(x))
  95. # define argumentsAt(n) *(arg+n)
  96. # define temporaryAt(n) *(temps+n)
  97. # define temporaryAtPut(n,x) decr(temporaryAt(n)); incr(temporaryAt(n)=(x))
  98. # define literalsAt(n) *(lits+n)
  99. # define contextAt(n) *(cntx+n)
  100. # define contextAtPut(n,x) incr(contextAt(n-1)=(x))
  101. # define processStackAt(n) *(psb+(n-1))
  102.  
  103.  
  104. /* the following are manipulated by primitives */
  105. object     processStack;
  106. int     linkPointer;
  107.  
  108. static object growProcessStack(top, toadd)
  109. int top, toadd;
  110. {    int size, i;
  111.     object newStack;
  112.  
  113.     if (toadd < 100) toadd = 100;
  114.     size = sizeField(processStack) + toadd;
  115.     newStack = newArray(size);
  116.     for (i = 1; i <= top; i++) {
  117.         basicAtPut(newStack, i, basicAt(processStack, i));
  118.         }
  119.     return newStack;
  120. }
  121.  
  122. boolean execute(aProcess, maxsteps)
  123. object aProcess;
  124. int maxsteps;
  125. {   object returnedObject;
  126.     int returnPoint, timeSliceCounter;
  127.     object *pst, *psb, *rcv, *arg, *temps, *lits, *cntx;
  128.     object contextObject, *primargs;
  129.     int byteOffset;
  130.     object methodClass, argarray;
  131.     int i, j;
  132.     register int low;
  133.     int high;
  134.     register object incrobj;  /* speed up increments and decrements */
  135.     byte *bp;
  136.  
  137.     /* unpack the instance variables from the process */
  138.     processStack    = basicAt(aProcess, stackInProcess);
  139.     psb = sysMemPtr(processStack);
  140.     j = intValue(basicAt(aProcess, stackTopInProcess));
  141.     pst = psb + (j-1);
  142.     linkPointer     = intValue(basicAt(aProcess, linkPtrInProcess));
  143.  
  144.     /* set the process time-slice counter before entering loop */
  145.     timeSliceCounter = maxsteps;
  146.  
  147.     /* retrieve current values from the linkage area */
  148.   readLinkageBlock:
  149.     contextObject  = processStackAt(linkPointer+1);
  150.     returnPoint = intValue(processStackAt(linkPointer+2));
  151.     byteOffset  = intValue(processStackAt(linkPointer+4));
  152.     if (contextObject == nilobj) {
  153.     contextObject = processStack;
  154.     cntx = psb;
  155.         arg = cntx + (returnPoint-1);
  156.     method      = processStackAt(linkPointer+3);
  157.     temps = cntx + linkPointer + 4;
  158.     }
  159.     else {    /* read from context object */
  160.     cntx = sysMemPtr(contextObject);
  161.     method = basicAt(contextObject, methodInContext);
  162.     arg = sysMemPtr(basicAt(contextObject, argumentsInContext));
  163.     temps = sysMemPtr(basicAt(contextObject, temporariesInContext));
  164.     }
  165.  
  166.     if (! isInteger(argumentsAt(0)))
  167.         rcv = sysMemPtr(argumentsAt(0));
  168.  
  169.   readMethodInfo:
  170.     lits           = sysMemPtr(basicAt(method, literalsInMethod));
  171.     bp             = bytePtr(basicAt(method, bytecodesInMethod)) - 1;
  172.  
  173.     while ( --timeSliceCounter > 0 ) {
  174.         low = (high = nextByte()) & 0x0F;
  175.         high >>= 4;
  176.         if (high == 0) {
  177.             high = low;
  178.             low = nextByte();
  179.             }
  180.  
  181. # if 0
  182.     if (debugging) {
  183.     fprintf(stderr,"method %s %d ",charPtr(basicAt(method, messageInMethod)), byteOffset);
  184.     fprintf(stderr,"stack %d %d ",pst, *pst);
  185.     fprintf(stderr,"executing %d %d\n", high, low);
  186.     }
  187. # endif
  188.         switch(high) {
  189.  
  190.             case PushInstance:
  191.                 ipush(receiverAt(low));
  192.                 break;
  193.  
  194.             case PushArgument:
  195.         ipush(argumentsAt(low));
  196.                 break;
  197.  
  198.             case PushTemporary:
  199.                 ipush(temporaryAt(low));
  200.                 break;
  201.  
  202.             case PushLiteral:
  203.                 ipush(literalsAt(low));
  204.                 break;
  205.  
  206.             case PushConstant:
  207.                 switch(low) {
  208.                     case 0: case 1: case 2:
  209.                         ipush(newInteger(low));
  210.                         break;
  211.  
  212.                     case minusOne:
  213.                         ipush(newInteger(-1));
  214.                         break;
  215.  
  216.                     case contextConst:
  217.                         /* check to see if we have made a block context yet */
  218.                         if (contextObject == processStack) {
  219.                             /* not yet, do it now - first get real return point */
  220.                 returnPoint = intValue(processStackAt(linkPointer+2));
  221.                 contextObject = newContext(linkPointer, method,
  222.                 copyFrom(processStack, returnPoint, 
  223.                         linkPointer - returnPoint),
  224.                 copyFrom(processStack, linkPointer + 5,
  225.                         methodTempSize(method)));
  226.                             basicAtPut(processStack, linkPointer+1, contextObject);
  227.                 ipush(contextObject);
  228.                 /* save byte pointer then restore things properly */
  229.                 fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  230.                 goto readLinkageBlock;
  231.  
  232.                             }
  233.                         ipush(contextObject);
  234.                         break;
  235.  
  236.                     case nilConst: 
  237.                         ipush(nilobj);
  238.                         break;
  239.  
  240.                     case trueConst:
  241.                         ipush(trueobj);
  242.                         break;
  243.  
  244.                     case falseConst:
  245.                         ipush(falseobj);
  246.                         break;
  247.  
  248.                     default:
  249.                         sysError("unimplemented constant","pushConstant");
  250.                     }
  251.                 break;
  252.  
  253.             case AssignInstance:
  254.         receiverAtPut(low, stackTop());
  255.                 break;
  256.  
  257.             case AssignTemporary:
  258.                 temporaryAtPut(low, stackTop());
  259.                 break;
  260.  
  261.             case MarkArguments:
  262.                 returnPoint = (processStackTop() - low) + 1;
  263.         timeSliceCounter++;    /* make sure we do send */
  264.                 break;
  265.  
  266.             case SendMessage:
  267.                 messageToSend = literalsAt(low);
  268.  
  269.          doSendMessage:
  270.         arg = psb + (returnPoint-1);
  271.         if (isInteger(argumentsAt(0)))
  272.             /* should fix this later */
  273.             methodClass = getClass(argumentsAt(0));
  274.         else {
  275.             rcv = sysMemPtr(argumentsAt(0));
  276.                     methodClass = classField(argumentsAt(0));
  277.             }
  278.  
  279.          doFindMessage:
  280.                 /* look up method in cache */
  281.         i = (((int) messageToSend) + ((int) methodClass)) % cacheSize;
  282.         if ((methodCache[i].cacheMessage == messageToSend) &&
  283.             (methodCache[i].lookupClass == methodClass)) {
  284.             method = methodCache[i].cacheMethod;
  285.             methodClass = methodCache[i].cacheClass;
  286.             }
  287.         else {
  288.             methodCache[i].lookupClass = methodClass;
  289.                     if (! findMethod(&methodClass)) {
  290.             /* not found, we invoke a smalltalk method */
  291.             /* to recover */
  292.             j = processStackTop() - returnPoint;
  293.             argarray = newArray(j+1);
  294.             for (; j >= 0; j--) {
  295.                 ipop(returnedObject);
  296.                 basicAtPut(argarray, j+1, returnedObject);
  297.                 decr(returnedObject);
  298.                 }
  299.             ipush(basicAt(argarray, 1)); /* push receiver back */
  300.             ipush(messageToSend);
  301.             messageToSend = newSymbol("message:notRecognizedWithArguments:");
  302.             ipush(argarray);
  303.             /* try again - if fail really give up */
  304.             if (! findMethod(&methodClass)) {
  305.                 sysWarn("can't find","error recovery method");
  306.                 /* just quit */
  307.                 return false;
  308.                 }
  309.             }
  310.             methodCache[i].cacheMessage = messageToSend;
  311.             methodCache[i].cacheMethod = method;
  312.             methodCache[i].cacheClass = methodClass;
  313.             }
  314.  
  315.         if (watching && (basicAt(method, watchInMethod) != nilobj)) {
  316.             /* being watched, we send to method itself */
  317.             j = processStackTop() - returnPoint;
  318.             argarray = newArray(j+1);
  319.             for (; j >= 0; j--) {
  320.                 ipop(returnedObject);
  321.                 basicAtPut(argarray, j+1, returnedObject);
  322.                 decr(returnedObject);
  323.                 }
  324.             ipush(method); /* push method */
  325.             ipush(argarray);
  326.             messageToSend = newSymbol("watchWith:");
  327.             /* try again - if fail really give up */
  328.                         methodClass = classField(method);
  329.             if (! findMethod(&methodClass)) {
  330.                 sysWarn("can't find","watch method");
  331.                 /* just quit */
  332.                 return false;
  333.                 }
  334.             }
  335.  
  336.         /* save the current byte pointer */
  337.                 fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  338.  
  339.         /* make sure we have enough room in current process */
  340.         /* stack, if not make stack larger */
  341.         i = 6 + methodTempSize(method) + methodStackSize(method);
  342.         j = processStackTop();
  343.                 if ((j + i) > sizeField(processStack)) {
  344.                     processStack = growProcessStack(j, i);
  345.             psb = sysMemPtr(processStack);
  346.             pst = (psb + j);
  347.                     fieldAtPut(aProcess, stackInProcess, processStack);
  348.                     }
  349.  
  350.         byteOffset = 1;
  351.                     /* now make linkage area */
  352.             /* position 0 : old linkage pointer */
  353.                 ipush(newInteger(linkPointer));
  354.                 linkPointer = processStackTop();
  355.             /* position 1 : context object (nil means stack) */
  356.                 ipush(nilobj);
  357.         contextObject = processStack;
  358.         cntx = psb;
  359.             /* position 2 : return point */
  360.                 ipush(newInteger(returnPoint));
  361.         arg = cntx + (returnPoint-1);
  362.             /* position 3 : method */
  363.                 ipush(method);
  364.             /* position 4 : bytecode counter */
  365.                 ipush(newInteger(byteOffset));
  366.             /* then make space for temporaries */
  367.         temps = pst+1;
  368.                 pst += methodTempSize(method);
  369.         /* break if we are too big and probably looping */
  370.         if (sizeField(processStack) > 1800) timeSliceCounter = 0;
  371.                 goto readMethodInfo; 
  372.  
  373.             case SendUnary:
  374.         /* do isNil and notNil as special cases, since */
  375.         /* they are so common */
  376.         if ((! watching) && (low <= 1)) {
  377.             if (stackTop() == nilobj) {
  378.                 stackTopPut((low?falseobj:trueobj));
  379.             break;
  380.             }
  381.             }
  382.         returnPoint = processStackTop();
  383.         messageToSend = unSyms[low];
  384.                 goto doSendMessage;
  385.                 break;
  386.  
  387.             case SendBinary:
  388.                 /* optimized as long as arguments are int */
  389.                 /* and conversions are not necessary */
  390.                 /* and overflow does not occur */
  391.                 if ((! watching) && (low <= 12)) {
  392.             primargs = pst - 1;
  393.             returnedObject = primitive(low+60, primargs);
  394.             if (returnedObject != nilobj) {
  395.             /* pop arguments off stack , push on result */
  396.             stackTopFree();
  397.             stackTopPut(returnedObject);
  398.                 break;
  399.             }
  400.                     }
  401.                 /* else we do it the old fashion way */
  402.         returnPoint = processStackTop() - 1;
  403.         messageToSend = binSyms[low];
  404.                 goto doSendMessage;
  405.  
  406.             case DoPrimitive:
  407.         /* low gives number of arguments */
  408.         /* next byte is primitive number */
  409.         primargs = (pst - low) + 1;
  410.         /* next byte gives primitive number */
  411.         i = nextByte();
  412.         /* a few primitives are so common, and so easy, that
  413.            they deserve special treatment */
  414.         switch(i) {
  415.             case 5:    /* set watch */
  416.             watching = ! watching;
  417.             returnedObject = watching?trueobj:falseobj;
  418.             break;
  419.  
  420.             case 11: /* class of object */
  421.             returnedObject = getClass(*primargs);
  422.             break;
  423.             case 21: /* object equality test */
  424.             if (*primargs == *(primargs+1))
  425.                returnedObject = trueobj;
  426.             else returnedObject = falseobj;
  427.             break;
  428.             case 25: /* basicAt: */
  429.             j = intValue(*(primargs+1));
  430.             returnedObject = basicAt(*primargs, j);
  431.             break;
  432.             case 31: /* basicAt:Put:*/
  433.             j = intValue(*(primargs+1));
  434.             fieldAtPut(*primargs, j, *(primargs+2));
  435.             returnedObject = nilobj;
  436.             break;
  437.             case 53: /* set time slice */
  438.             timeSliceCounter = intValue(*primargs);
  439.             returnedObject = nilobj;
  440.             break;
  441.             case 58: /* allocObject */
  442.             j = intValue(*primargs);
  443.             returnedObject = allocObject(j);
  444.             break;
  445.             case 87: /* value of symbol */
  446.             returnedObject = globalSymbol(charPtr(*primargs));
  447.             break;
  448.             default: 
  449.             returnedObject = primitive(i, primargs); break;
  450.             }
  451.         /* increment returned object in case pop would destroy it */
  452.         incr(returnedObject);
  453.         /* pop off arguments */
  454.                 while (low-- > 0) {
  455.             stackTopFree();
  456.             }
  457.         /* returned object has already been incremented */
  458.         ipush(returnedObject); decr(returnedObject);
  459.         break;
  460.  
  461.              doReturn:
  462.                 returnPoint = intValue(basicAt(processStack, linkPointer + 2));
  463.                 linkPointer = intValue(basicAt(processStack, linkPointer));
  464.                 while (processStackTop() >= returnPoint) {
  465.             stackTopFree();
  466.                     }
  467.         /* returned object has already been incremented */
  468.         ipush(returnedObject); decr(returnedObject);
  469.         /* now go restart old routine */
  470.         if (linkPointer != nilobj)
  471.                     goto readLinkageBlock;
  472.         else
  473.             return false /* all done */;
  474.  
  475.             case DoSpecial:
  476.                 switch(low) {
  477.                     case SelfReturn:
  478.                         incr(returnedObject = argumentsAt(0));
  479.                         goto doReturn;
  480.  
  481.                     case StackReturn:
  482.                         ipop(returnedObject);
  483.             goto doReturn;
  484.  
  485.                     case Duplicate:
  486.             /* avoid possible subtle bug */
  487.                         returnedObject = stackTop();
  488.                         ipush(returnedObject);
  489.                         break;
  490.  
  491.                     case PopTop:
  492.                         ipop(returnedObject);
  493.                         decr(returnedObject);
  494.                         break;
  495.  
  496.                     case Branch:
  497.                         /* avoid a subtle bug here */
  498.                         i = nextByte();
  499.                         byteOffset = i;
  500.                         break;
  501.  
  502.                     case BranchIfTrue:
  503.                         ipop(returnedObject);
  504.                         i = nextByte();
  505.                         if (returnedObject == trueobj) {
  506.                             /* leave nil on stack */
  507.                 pst++;
  508.                             byteOffset = i;
  509.                             }
  510.                         decr(returnedObject);
  511.                         break;
  512.  
  513.                     case BranchIfFalse:
  514.                         ipop(returnedObject);
  515.                         i = nextByte();
  516.                         if (returnedObject == falseobj) {
  517.                             /* leave nil on stack */
  518.                 pst++;
  519.                             byteOffset = i;
  520.                             }
  521.                         decr(returnedObject);
  522.                         break;
  523.  
  524.                     case AndBranch:
  525.                         ipop(returnedObject);
  526.                         i = nextByte();
  527.                         if (returnedObject == falseobj) {
  528.                             ipush(returnedObject);
  529.                             byteOffset = i;
  530.                             }
  531.                         decr(returnedObject);
  532.                         break;
  533.  
  534.                     case OrBranch:
  535.                         ipop(returnedObject);
  536.                         i = nextByte();
  537.                         if (returnedObject == trueobj) {
  538.                             ipush(returnedObject);
  539.                             byteOffset = i;
  540.                             }
  541.                         decr(returnedObject);
  542.                         break;
  543.  
  544.                     case SendToSuper:
  545.             i = nextByte();
  546.             messageToSend = literalsAt(i);
  547.             rcv = sysMemPtr(argumentsAt(0));
  548.             methodClass = basicAt(method, methodClassInMethod);
  549.             /* if there is a superclass, use it
  550.                otherwise for class Object (the only 
  551.                class that doesn't have a superclass) use
  552.                the class again */
  553.             returnedObject = 
  554.                   basicAt(methodClass, superClassInClass);
  555.             if (returnedObject != nilobj)
  556.                 methodClass = returnedObject;
  557.             goto doFindMessage;
  558.  
  559.                     default:
  560.                         sysError("invalid doSpecial","");
  561.                         break;
  562.                 }
  563.                 break;
  564.  
  565.             default:
  566.                 sysError("invalid bytecode","");
  567.                 break;
  568.         }
  569.     }
  570.  
  571.     /* before returning we put back the values in the current process */
  572.     /* object */
  573.  
  574.     fieldAtPut(processStack, linkPointer+4, newInteger(byteOffset));
  575.     fieldAtPut(aProcess, stackTopInProcess, newInteger(processStackTop()));
  576.     fieldAtPut(aProcess, linkPtrInProcess, newInteger(linkPointer));
  577.  
  578.     return true;
  579. }
  580.